package com.immediatus.engine.util;

import android.util.FloatMath;

public class Transformation{
    private static final float DEG_TO_RAD = (float) Math.PI / 180.0f;

    private float a = 1.0f;
    private float b = 0.0f;
    private float c = 0.0f;
    private float d = 1.0f;
    private float tx = 0.0f;
    private float ty = 0.0f;

    public Transformation(){
    }

    public void reset(){
        this.setToIdentity();
    }

    public void setToIdentity(){
        this.a = 1.0f;
        this.d = 1.0f;

        this.b = 0.0f;
        this.c = 0.0f;
        this.tx = 0.0f;
        this.ty = 0.0f;
    }

    public void setTo(final Transformation transformation_){
        this.a = transformation_.a;
        this.d = transformation_.d;

        this.b = transformation_.b;
        this.c = transformation_.c;
        this.tx = transformation_.tx;
        this.ty = transformation_.ty;
    }

    public void preTranslate(final float x_, final float y_){
        this.tx += x_ * this.a + y_ * this.c;
        this.ty += x_ * this.b + y_ * this.d;
    }

    public void postTranslate(final float x_, final float y_){
        this.tx += x_;
        this.ty += y_;
    }

    public Transformation setToTranslate(final float x_, final float y_){
        this.a = 1.0f;
        this.b = 0.0f;
        this.c = 0.0f;
        this.d = 1.0f;
        this.tx = x_;
        this.ty = y_;

        return this;
    }

    public void preScale(final float sx_, final float sy_){
        this.a *= sx_;
        this.b *= sx_;
        this.c *= sy_;
        this.d *= sy_;
    }

    public void postScale(final float sx_, final float sy_){
        this.a = this.a * sx_;
        this.b = this.b * sy_;
        this.c = this.c * sx_;
        this.d = this.d * sy_;
        this.tx = this.tx * sx_;
        this.ty = this.ty * sy_;
    }

    public Transformation setToScale(final float sx_, final float sy_){
        this.a = sx_;
        this.b = 0.0f;
        this.c = 0.0f;
        this.d = sy_;
        this.tx = 0.0f;
        this.ty = 0.0f;

        return this;
    }

    public void preRotate(final float a_){
        final float angleRad = degToRad(a_);

        final float sin = FloatMath.sin(angleRad);
        final float cos = FloatMath.cos(angleRad);

        final float a = this.a;
        final float b = this.b;
        final float c = this.c;
        final float d = this.d;

        this.a = cos * a + sin * c;
        this.b = cos * b + sin * d;
        this.c = cos * c - sin * a;
        this.d = cos * d - sin * b;
    }

    public void postRotate(final float a_){
        final float angleRad = degToRad(a_);

        final float sin = FloatMath.sin(angleRad);
        final float cos = FloatMath.cos(angleRad);

        final float a = this.a;
        final float b = this.b;
        final float c = this.c;
        final float d = this.d;
        final float tx = this.tx;
        final float ty = this.ty;

        this.a = a * cos - b * sin;
        this.b = a * sin + b * cos;
        this.c = c * cos - d * sin;
        this.d = c * sin + d * cos;
        this.tx = tx * cos - ty * sin;
        this.ty = tx * sin + ty * cos;
    }

    public Transformation setToRotate(final float a_){
        final float angleRad = degToRad(a_);

        final float sin = FloatMath.sin(angleRad);
        final float cos = FloatMath.cos(angleRad);

        this.a = cos;
        this.b = sin;
        this.c = -sin;
        this.d = cos;
        this.tx = 0.0f;
        this.ty = 0.0f;

        return this;
    }

    public void postConcat(final Transformation t_){
        this.postConcat(t_.a, t_.b, t_.c, t_.d, t_.tx, t_.ty);
    }

    private void postConcat(final float a_, final float b_, final float c_, final float d_, final float tx_, final float ty_){
        final float a = this.a;
        final float b = this.b;
        final float c = this.c;
        final float d = this.d;
        final float tx = this.tx;
        final float ty = this.ty;

        this.a = a * a_ + b * c_;
        this.b = a * b_ + b * d_;
        this.c = c * a_ + d * c_;
        this.d = c * b_ + d * d_;
        this.tx = tx * a_ + ty * c_ + tx_;
        this.ty = tx * b_ + ty * d_ + ty_;
    }

    public void preConcat(final Transformation t_){
        this.preConcat(t_.a, t_.b, t_.c, t_.d, t_.tx, t_.ty);
    }

    private void preConcat(final float a_, final float b_, final float c_, final float d_, final float tx_, final float ty_){
        final float a = this.a;
        final float b = this.b;
        final float c = this.c;
        final float d = this.d;
        final float tx = this.tx;
        final float ty = this.ty;

        this.a = a_ * a + b_ * c;
        this.b = a_ * b + b_ * d;
        this.c = c_ * a + d_ * c;
        this.d = c_ * b + d_ * d;
        this.tx = tx_ * a + ty_ * c + tx;
        this.ty = tx_ * b + ty_ * d + ty;
    }

    public void transform(final float[] vertices_){
        int count = vertices_.length >> 1;
        int i = 0;
        int j = 0;
        while (--count >= 0){
            final float x = vertices_[i++];
            final float y = vertices_[i++];
            vertices_[j++] = x * this.a + y * this.c + this.tx;
            vertices_[j++] = x * this.b + y * this.d + this.ty;
        }
    }

    private static float degToRad(final float degree_){
        return DEG_TO_RAD * degree_;
    }
}